using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Drawing;
using System.Globalization;
using System.Reflection;
using System.Security.Principal;
using System.Threading;
using System.Windows.Forms;
using SqlServerDRControlPanel;
using SqlServerDRControlPanel.MirroringGroup;

[assembly: CLSCompliant(true)]
namespace AutomatedDatabaseDR
{
    public partial class MainInterface : Form
    {
        private static bool m_UserIsSecure;
        private static string m_SecurityGroup;
        private DataTable m_SvrList;
        private bool m_AllChecked;
        private string m_UserName = "";
        private string m_DatabaseName;
        private string m_PDCServer;
        private string m_DRServer;
        private List<MirroringGroup> m_PollThread = new List<MirroringGroup>();

        #region Constructor
        public MainInterface()
        {
            InitializeComponent();
            string ActionText = "Select an action to take on the\r\nselected database(s)";
            label4.Text = ActionText;
        }
        #endregion

        #region Load Configuration Data from Database

        /// <summary>
        /// Method used to collect all configuration from the configuration database.
        /// </summary>
        private void GetConfig()
        {
            string ConfigServer = ConfigurationManager.AppSettings["ConfigDbConn"];
            string[] ConfigItems = ConfigServer.Split('|');
            ConfigItems ci = new ConfigItems(ConfigItems[0], ConfigItems[1]);

            try
            {
                GridViewAppList.AutoGenerateColumns = false;
                m_SvrList = ci.RestoreList(ci.ServerList().Tables[0]);
                GridViewAppList.DataSource = m_SvrList;
                if (GridViewAppList.ColumnCount < 2)
                {
                    DataGridViewCell cell = new DataGridViewTextBoxCell();
                    DataGridViewCell cell2 = new DataGridViewTextBoxCell();
                    cell2.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
                    DataGridViewColumn DbName = new DataGridViewColumn(cell);
                    DbName.HeaderText = "Database Name";
                    DataGridViewColumn PdcSvrName = new DataGridViewColumn(cell);
                    PdcSvrName.HeaderText = "Primary Server";
                    DataGridViewColumn DrSvrName = new DataGridViewColumn(cell);

                    DataGridViewColumn DirectionColumn = new DataGridViewColumn(cell2);
                    DirectionColumn.HeaderText = "Direction";

                    DrSvrName.HeaderText = "Mirror Server";
                    DataGridViewColumn DrType = new DataGridViewColumn(cell);
                    DrType.HeaderText = "Type";
                    DataGridViewColumn StatusCol = new DataGridViewColumn(cell);
                    StatusCol.HeaderText = "Status";

                    DbName.DataPropertyName = "DatabaseName";
                    PdcSvrName.DataPropertyName = "PdcServer";
                    DirectionColumn.DataPropertyName = "Direction";
                    
                    DrSvrName.DataPropertyName = "DrServer";
                    DrType.DataPropertyName = "DrType";
                    StatusCol.DataPropertyName = "Status";

                    GridViewAppList.Columns.Add(DbName);
                    GridViewAppList.Columns.Add(PdcSvrName);
                    GridViewAppList.Columns.Add(DirectionColumn);
                    GridViewAppList.Columns.Add(DrSvrName);
                    GridViewAppList.Columns.Add(DrType);
                    GridViewAppList.Columns.Add(StatusCol);
                    
                    // This does not work
                    //GridViewAppList.Columns.Add("DatabaseName", "Database Name");
                    //GridViewAppList.Columns.Add("PdcServer", "Primary Server");
                    //GridViewAppList.Columns.Add("DrServer", "Mirror Server");
                    //GridViewAppList.Columns.Add("DrType", "DR Type");
                    //GridViewAppList.Columns.Add("Status", "Status");
                }
                //PollForStatusThreaded();
            }
            catch (System.Data.SqlClient.SqlException se)
            {
                if (se.ToString().Contains("An error has occurred while establishing a connection to the server."))
                {
                    MessageBox.Show("Unable to connect to server\r\nCheck the application.config file.", "Failure getting config data");
                    Application.Exit();
                }
                else
                {
                    MessageBox.Show(se.Message, "Failure getting config data");
                }
                
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + ex.Source, "Failure getting config data");
            }
        }
        #endregion

        #region Exec DR methods
        /// <summary>
        /// Code that takes the selected actions from the form and instanciates the failoverActions class
        /// and multithreads the work.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Button_exec_DR_Click(object sender, EventArgs e)
        {
            if (!IsAnyCheckBoxChecked())
            {
                MessageBox.Show(this, "No databases selected", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            DialogResult result;
            result = MessageBox.Show(@"Are you sure that you want initiate DR for the selected databases? 
            This operation cannot be cancelled.", "Caution", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
            try
            {
                if (result == DialogResult.No) { return; }

                for (int i = 0; i < GridViewAppList.RowCount; i++)
                {
                    bool cbstate1 = false;
                    Object cbstate = this.GridViewAppList.Rows[i].Cells[0].Value;
                    
                    if (cbstate == null)
                    {
                        cbstate1 = false;
                    }
                    else
                    {
                        cbstate1 = Convert.ToBoolean(cbstate);
                    }
                    if (cbstate1)
                    {
                        GridViewAppList.Rows[i].DefaultCellStyle.ForeColor = Color.Black;
                        GridViewAppList.Rows[i].Cells[6].Value = "Starting";
                        GridViewAppList.Rows[i].Cells[0].Value = null;
                        m_DatabaseName = GridViewAppList.Rows[i].Cells[1].Value.ToString();
                        m_PDCServer = GridViewAppList.Rows[i].Cells[2].Value.ToString();
                        m_DRServer = GridViewAppList.Rows[i].Cells[4].Value.ToString();

                        switch (GetDrTypeInput())
                        {
                            case "FailToDR":
                                ExecuteAction("Fail2DR", i);
                                break;
                            case "FailToPDC":
                                ExecuteAction("Fail2PDC", i);
                                break;
                            case "CutToDR":
                                ExecuteAction("Cut2DR", i);
                                break;
                            case "RemoveMirroring":
                                ExecuteAction("RemoveMirroring", i);
                                break;
                            default:
                                MessageBox.Show("Could not get Action.", "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
                                break;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                string error = ex.Message;
                textBox1.Text = error;
                textBox1.Visible = true;
            }
        }
        
        /// <summary>
        /// Checks to see if any datagrid checkboxes are checked, returns true on the first 
        /// occurance of a checked box.
        /// </summary>
        /// <returns></returns>
        private bool IsAnyCheckBoxChecked()
        {
            for (int i = 0; i < GridViewAppList.RowCount; i++)
            {
                Object cbstate = this.GridViewAppList.Rows[i].Cells[0].Value;
                if (Convert.ToBoolean(cbstate))
                {
                    return true;
                }
            }
            return false;

        }
       
        /// <summary>
        /// Action method to start processing.
        /// </summary>
        /// <param name="Command"></param>
        private void ExecuteAction(string Command, int RowNumber)
        {
            MirroringGroup fa = new MirroringGroup(m_DatabaseName, RowNumber);
            fa.MirroringGroupEvents += new EventHandler<MirroringGroupEventArgs>(fa_ClassEvents);
            fa.MirroringGroupCompletedEvent += new EventHandler<MirroringGroupCompletedEventArgs>(fa_MirroringGroupCompletedEvent);
            // string internalCmd = Command.ToUpper(CultureInfo.InvariantCulture);
            Thread th;

            switch (Command.ToUpper(CultureInfo.InvariantCulture))
            {
                case "FAIL2DR":
                    fa.PrincipleServerName = m_PDCServer;
                    fa.MirrorServerName = m_DRServer;
                    th = new Thread(new ThreadStart(fa.FailoverDatabase));
                    th.Start();
                    break;
                case "FAIL2PDC":
                    fa.PrincipleServerName = m_DRServer;
                    fa.MirrorServerName = m_PDCServer;
                    th = new Thread(new ThreadStart(fa.FailoverDatabase));
                    th.Start();
                    break;
                case "CUT2DR":
                    fa.MirrorServerName = m_DRServer;
                    th = new Thread(new ThreadStart(fa.ForceMirrorOnline));
                    th.Start();
                    break;
                case "REMOVEMIRRORING":
                    fa.PrincipleServerName = m_PDCServer;
                    fa.MirrorServerName = m_DRServer;
                    th = new Thread(new ThreadStart(fa.RemoveMirroring));
                    th.Start();
                    break;
            }
        }

        

        #endregion

        #region Threadsafe objects for thread status

        delegate void UpdateGridStatusDelegate(object[] msg);
        UpdateGridStatusDelegate threadSafeStatusDelegate;

        delegate void MirroringGroupThreadCompletedDelegate(int msg);
        MirroringGroupThreadCompletedDelegate threadSafeCompletedDelegate;

        /// <summary>
        /// Threadsafe Event Handler for handling the status of failover actions.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void fa_ClassEvents(object sender, MirroringGroupEventArgs e)
        {
            threadSafeStatusDelegate = new UpdateGridStatusDelegate(UpdateGridStatus);

            object[] instr = new object[2] { e.RowNumber, e.Message };
            this.Invoke(threadSafeStatusDelegate, new object[] { instr });
        }
        
        
        public void UpdateGridStatus(object[] msg)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(threadSafeStatusDelegate, msg);
            }
            
            GridViewAppList.Rows[(int)msg[0]].Cells[6].Value = (string)msg[1];
        }

        void fa_MirroringGroupCompletedEvent(object sender, MirroringGroupCompletedEventArgs e)
        {
            threadSafeCompletedDelegate = new MirroringGroupThreadCompletedDelegate(OnCompletedPoll);
            this.Invoke(threadSafeCompletedDelegate, e.RowNumber);
            //sender = null;
        }

        public void OnCompletedPoll(int value)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(threadSafeCompletedDelegate, value);
            }
            PollForStatusThreaded();
        }

        #endregion

        #region Form Operation
        /// <summary>
        /// On load
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Load(object sender, EventArgs e)
        {
            m_UserName = WindowsIdentity.GetCurrent().Name;
            m_SecurityGroup = ConfigurationManager.AppSettings["SecurityGroup"];
            CheckSecurityGroups();
            LabelUser.Text = m_UserName;
            SetToolTips();
            GetConfig();
            SetStatusBarTxt();
            GridViewAppList.ClearSelection();
        }

        /// <summary>
        /// Check that the user running process is in a domain group with permissions to manage
        /// database mirroring, restore databases and execute scripts.
        /// </summary>
        private void CheckSecurityGroups()
        {
            WindowsPrincipal wp = new WindowsPrincipal(WindowsIdentity.GetCurrent());
            m_UserIsSecure = true; // wp.IsInRole(m_SecurityGroup);
            if (m_UserIsSecure)
            {
                return;
            }
            MessageBox.Show(this, @"Your user account is not in a group 
                                    with appropriate permissions, 
                                    access will be limited.", "Caution", 
                                    MessageBoxButtons.OK,
                                    MessageBoxIcon.Exclamation);
            Button_exec_DR.Enabled = false;
            Button_PollStatus.Enabled = false;
        }

        /// <summary>
        /// Quick function that creates the status bar text.
        /// </summary>
        private void SetStatusBarTxt()
        {
            Version ver = Assembly.GetExecutingAssembly().GetName().Version;
            string ServerCount = GridViewAppList.Rows.Count.ToString();
            toolStripStatusLabel1.Text = "Version: " + ver.ToString() + "      Total databases " + ServerCount;
        }
        /// <summary>
        /// Setup the tool tips.
        /// </summary>
        private void SetToolTips()
        {
            ToolTip DRFail = new ToolTip();
            DRFail.SetToolTip(RbDrFailover, "Failover to DR and mirror back to the PDC.");
            ToolTip PDCFail = new ToolTip();
            PDCFail.SetToolTip(RbFailToPdc, "Failover to PDC and mirror to DR.");
            ToolTip CutDR = new ToolTip();
            CutDR.SetToolTip(RbPdcBlack, "Use only when Principle is not available.\r\nRemoves mirror from DR server sets recovery.");
            ToolTip RemMirr = new ToolTip();
            RemMirr.SetToolTip(RbRemoveMirror, "Remove mirroring and setup the DR database as active.");
        }
        /// <summary>
        /// Select all items in the datagrid
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void checkall_Button_Click(object sender, EventArgs e)
        {
            if (!(m_AllChecked))
            {
                for (int i = 0; i < GridViewAppList.Rows.Count; i++)
                {
                    GridViewAppList.Rows[i].Cells[0].Value = "true";
                    checkall_Button.Text = "Deselect All";
                    m_AllChecked = true;
                }
            }
            else
            {
                for (int i = 0; i < GridViewAppList.Rows.Count; i++)
                {
                    GridViewAppList.Rows[i].Cells[0].Value = "false";
                    checkall_Button.Text = "Select All";
                    m_AllChecked = false;
                }
            }
        }

        /// <summary>
        /// This will check the form to see what DR action is set. 
        /// Then it will return a string to the caller.
        /// </summary>
        private string GetDrTypeInput()
        {

            string RetStr = "";
            if (RbDrFailover.Checked)
            {
                RetStr = "FailToDR";
            }
            else if (RbFailToPdc.Checked)
            {
                RetStr = "FailToPDC";
            }
            else if (RbPdcBlack.Checked)
            {
                RetStr = "CutToDR";
            }
            else if (RbRemoveMirror.Checked)
            {
                RetStr = "RemoveMirroring";
            }
            else { RetStr = "Nothing"; }

            return RetStr;
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Version ver = Assembly.GetExecutingAssembly().GetName().Version;
            MessageBox.Show("Database DR Control Panel" + "\r\n" + "Version: " + ver,
                    "About", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        
        

        private void buildDatabaseTabesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CreateConfigDatabaseObjects ConfigObjects = new CreateConfigDatabaseObjects();
            ConfigObjects.Visible = true;
            ConfigObjects.Activate();
        }

        private void addApplicationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ManageApplications ManSvrs = new ManageApplications();
            ManSvrs.Visible = true;
            ManSvrs.Activate();
        }


        private void Rb_DrFailover_CheckedChanged(object sender, EventArgs e)
        {
            Button_exec_DR.Enabled = m_UserIsSecure;
        }

        private void RbFailToPdc_CheckedChanged(object sender, EventArgs e)
        {
            Button_exec_DR.Enabled = m_UserIsSecure;
        }

        private void RbPdcBlack_CheckedChanged(object sender, EventArgs e)
        {
            Button_exec_DR.Enabled = m_UserIsSecure;
        }

        private void RbRemoveMirror_CheckedChanged(object sender, EventArgs e)
        {
            Button_exec_DR.Enabled = m_UserIsSecure;
        }
        

        /// <summary>
        /// Reload gridview and datatable.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void reloadAppDataToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DialogResult result;
            result = MessageBox.Show(@"Refreshing the dataset will cause you to lose any status informaion in the DataGrid.", "Caution", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
            if (result == DialogResult.No) { return; }

            GetConfig();
            GridViewAppList.Refresh();
            SetStatusBarTxt();
        }

        private void PollServers_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            PollForStatusThreaded();
        }
        #endregion

        #region Poll Server Status methods
        
        /// <summary>
        /// Poll servers for the status of mirroring. Threaded.
        /// </summary>
        private void PollForStatusThreaded()
        {

            List<string> ServerList = new List<string>();
            pictureBox1.Visible = true;
            for (int i = 0; i < GridViewAppList.RowCount; i++)
            {
                string DatabaseName = GridViewAppList.Rows[i].Cells[1].Value.ToString();
                string PDCServer = GridViewAppList.Rows[i].Cells[2].Value.ToString();
                ServerList.Add(PDCServer + "|" + DatabaseName + "|" + i);
            }
            MirroringGroup fa = new MirroringGroup(ServerList);
            fa.MirroringGroupEvents += new EventHandler<SqlServerDRControlPanel.MirroringGroup.MirroringGroupEventArgs>(fa_ClassEvents_Poll);
            m_PollThread.Add(fa);

            Thread PollerTread = new Thread(new ThreadStart(fa.GetCurrentMirroringStatusAll));
            PollerTread.Start();
        }


        delegate void UpdateGridPollDelegate(string[] msg);
        UpdateGridPollDelegate threadSafePollDelegate;

       
       
        void fa_ClassEvents_Poll(object sender, SqlServerDRControlPanel.MirroringGroup.MirroringGroupEventArgs e)
        {
            MirroringGroup fa = m_PollThread[0];
            threadSafePollDelegate = new UpdateGridPollDelegate(UpdateGridPoll);
            if (fa.ThreadIsComplete)
            {
                foreach (string s in fa.returnDic)
                {
                    string[] instr = s.Split('|');
                    this.Invoke(threadSafePollDelegate, new object[] { instr });
                }

                m_PollThread.RemoveAt(0);
            }
        }


        void UpdateGridPoll(string[] msg)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(threadSafePollDelegate, msg);
            }
            
            string status = msg[1].ToUpper(CultureInfo.InvariantCulture);
            int i = Convert.ToInt16(msg[2]);

            if (status == "TRUE")
            {
                GridViewAppList.Rows[i].Cells[2].Style.ForeColor = Color.Green;
                GridViewAppList.Rows[i].Cells[3].Value = "==>";
                GridViewAppList.Rows[i].Cells[4].Style.ForeColor = Color.Orange;
                GridViewAppList.Refresh();
            }
            else if (status == "FALSE")
            {
                GridViewAppList.Rows[i].Cells[2].Style.ForeColor = Color.Orange;
                GridViewAppList.Rows[i].Cells[3].Value = "<==";
                GridViewAppList.Rows[i].Cells[4].Style.ForeColor = Color.Green;
                GridViewAppList.Refresh();
            }
            else if (status == "NOTMIRRORED")
            {
                GridViewAppList.Rows[i].Cells[2].Style.ForeColor = Color.DodgerBlue;
                GridViewAppList.Rows[i].Cells[3].Value = "NONE";
                GridViewAppList.Rows[i].Cells[3].Style.ForeColor = Color.DodgerBlue;
                GridViewAppList.Refresh();
            }
            else
            {
                GridViewAppList.Rows[i].DefaultCellStyle.ForeColor = Color.Red;
                GridViewAppList.Rows[i].Cells[3].Value = "!";
                GridViewAppList.Rows[i].DefaultCellStyle.ForeColor = Color.Red;
                GridViewAppList.Rows[i].Cells[6].Value = "ERROR Connecting to Database or Server";
                GridViewAppList.Refresh();
            }
            
            pictureBox1.Visible = false;
        }
        #endregion

    } // End Class
} // End NS